﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Data;
using C1.Phone.Chart;
using Microsoft.Phone.Controls;

namespace ControlExplorer.Controls.Chart
{
    public enum Position
    {
        North,
        South,
        East,
        West
    };

    public partial class PieSpin : UserControl, IExposePageOrientation
    {
        DoubleCollection _values;
        string[] _itemNames;
        RotateTransform _rot;
        bool _capture;
        double _mouseAngle;
        Point _ptDown, _ptMove;
        Position _position;
        int _selectedIndex;

        public PieSpin()
        {
            InitializeComponent();

            _selectedIndex = -1;
            _position = Position.South;

            // create transform to spin the chart
            pieChart.RenderTransformOrigin = new Point(.5, .5);
            _rot = new RotateTransform();
            pieChart.RenderTransform = _rot;

            // hook up mouse handlers
            pieChart.MouseLeftButtonDown += pieChart_MouseLeftButtonDown;
            pieChart.MouseLeftButtonUp += pieChart_MouseLeftButtonUp;
            pieChart.MouseMove += pieChart_MouseMove;

            // create sample data
            _values = new DoubleCollection();
            _values.Add(2122342);
            _values.Add(1269210);
            _values.Add(735358);
            _values.Add(310400);
            _values.Add(145898);
            _values.Add(115860);
            _values.Add(94060);
            _values.Add(93065);
            _itemNames = new string[] { "China", "USA", "Russia", "India", "Japan", "Canada", "Uruguay", "Brazil" };

            DataSeries ds = new DataSeries();
            ds.Values = _values;
            ds.PlotElementLoaded += ds_PlotElementLoaded;
            pieChart.Data.Children.Add(ds);
            pieChart.Data.ItemNames = _itemNames;

            RotateToSelected(1);
        }

        void ds_PlotElementLoaded(object sender, EventArgs e)
        {
            PieSlice slice = sender as PieSlice;
            if (slice != null)
            {
                slice.StrokeThickness = 0;
                slice.MouseLeftButtonDown += new MouseButtonEventHandler(slice_MouseLeftButtonDown);
                slice.MouseLeftButtonUp += new MouseButtonEventHandler(slice_MouseLeftButtonDown);
            }
        }

        void slice_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            PieSlice slice = sender as PieSlice;
            if (slice != null)
            {
                _selectedIndex = slice.DataPoint.PointIndex;
            }
        }


        void pieChart_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _capture = pieChart.CaptureMouse();
            _mouseAngle = GetMouseAngle(e);
            _ptDown = _ptMove = e.GetPosition(null);
        }
        void pieChart_MouseMove(object sender, MouseEventArgs e)
        {
            if (_capture)
            {
                // rotate the chart
                _rot.Angle = Math.IEEERemainder(_rot.Angle + GetMouseAngle(e) - _mouseAngle, 360);
                _mouseAngle = GetMouseAngle(e);
                _ptMove = e.GetPosition(null);

                // update selection REVIEW: not sure this is a good idea...
                //var index = GetClosestSlice();
                //_view.MoveCurrentToPosition(index);
            }
        }
        void pieChart_MouseLeftButtonUp(object sender, EventArgs e)
        {
            _capture = false;

            var index = -1;

            // check if this was a click or a drag
            if (Math.Abs(_ptDown.X - _ptMove.X) + Math.Abs(_ptDown.Y - _ptMove.Y) < 5)
            {
                // click: select the slice that was clicked
                foreach (var element in VisualTreeHelper.FindElementsInHostCoordinates(_ptDown, pieChart))
                {
                    var slice = element as PieSlice;
                    if (slice != null)
                    {
                        index = slice.DataPoint.PointIndex;
                        break;
                    }
                }
            }
            else
            {
                // drag: find slice that is closest to the current rotation angle
                index = GetClosestSlice();
            }

            // select the slice
            if (index > -1)
            {
                //_view.MoveCurrentToPosition(index);
                RotateToSelected(index);
            }

        }

        int GetClosestSlice()
        {
            var index = -1;
            var minDelta = 0.0;
            for (int i = 0; i < _values.Count; i++)
            {
                var delta = Math.Abs(_rot.Angle - GetSliceAngle(i));
                if (i == 0 || delta < minDelta)
                {
                    index = i;
                    minDelta = delta;
                }
            }
            return index;
        }

        // get the angle that corresponds to a given mouse position
        double GetMouseAngle(MouseEventArgs e)
        {
            var pt = e.GetPosition(pieChart);
            pt.X -= pieChart.ActualWidth / 2.0;
            pt.Y -= pieChart.ActualHeight / 2.0;
            var radians = Math.Atan2(pt.Y, pt.X);
            return Math.IEEERemainder(radians * 180.0 / Math.PI, 360);
        }

        // spin the chart to highlight current item
        void RotateToSelected(int index)
        {
            if (index > -1 && !_capture)
            {
                // calculate current and target angles
                var from = _rot.Angle;
                var to = GetSliceAngle(index);
                while (to - from > +180) to -= 360;
                while (to - from < -180) to += 360;
                if (to != from)
                {
                    // create animation
                    var da = new DoubleAnimation();
                    da.From = from;
                    da.To = to;
                    da.Duration = new Duration(TimeSpan.FromMilliseconds(600));
                    Storyboard.SetTargetProperty(da, new PropertyPath("Angle"));
                    Storyboard.SetTarget(da, _rot);

                    // add cool elastic ease effect
                    var ef = new ElasticEase();
                    ef.Oscillations = 1;
                    ef.Springiness = 3;
                    da.EasingFunction = ef;

                    // play animation
                    var sb = new Storyboard();
                    sb.Children.Add(da);
                    sb.Begin();

                    //// play sound
                    //if (ClickSound != null)
                    //{
                    //    this.ClickSound.Position = TimeSpan.Zero;
                    //    this.ClickSound.Play();
                    //}
                }

                // update labels
                txtName.Text = _itemNames[index];
                txtPopulation.Text = _values[index].ToString("n0");
            }
        }

        // get the angle needed to place a given slice at the selection point
        double GetSliceAngle(int index)
        {
            // calculate angles
            var cur = 0.0;
            var tot = 0.0;
            for (int i = 0; i < _values.Count; i++)
            {
                tot += _values[i];
                if (i < index)
                {
                    cur += _values[i];
                }
                else if (i == index)
                {
                    cur += _values[i] / 2;
                }
            }

            // calculate angle to align the selected slice
            var angle = -cur / tot * 360;
            switch (_position)
            {
                case Position.North:
                    angle += 270;
                    break;
                case Position.South:
                    angle += 90;
                    break;
                case Position.East:
                    angle += 0;
                    break;
                case Position.West:
                    angle += 180;
                    break;
            }

            // done 
            return Math.IEEERemainder(angle, 360);
        }

        #region IExposePageOrientation Members

        public SupportedPageOrientation Orientation
        {
            get
            {
                return SupportedPageOrientation.Portrait;
            }
        }

        #endregion
    }
}
